{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## _*Using Qiskit Aqua algorithms, a how to guide*_\n",
    "\n",
    "This notebook demonstrates how to use the `Qiskit Aqua` library to invoke an algorithm and process the result.\n",
    "\n",
    "Further information may be found for the algorithms in the online [Aqua documentation](https://qiskit.org/documentation/apidoc/qiskit.aqua.algorithms.html).\n",
    "\n",
    "Algorithms in Aqua can be created and run as usual in Python by constructing instances and calling methods.\n",
    "\n",
    "Aqua has many `algorithms` for solving different problems. For some we also have classical algorithms, that take the exact same input data, to solve the problem. This can be useful in the near term as Quantum algorithms are developed since we are still at a stage where we can do classical comparison of the result.\n",
    "\n",
    "Aqua also has various `components` which are dependent objects used by algorithms, such as variational forms, qfts, initial states etc. We will see more on this below.\n",
    "\n",
    "Lastly for developers we also have a collections of `circuits` and gates which can be used to help build out new components and algorithms.\n",
    "\n",
    "Here we will choose to show some of the main aspects of Aqua by solving a ground state energy problem."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit.aqua.operators import WeightedPauliOperator"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As input, for an energy problem, we need a Hamiltonian and so we first create a suitable `Operator ` instance. In this case we have a paulis list, as below, from a previously computed Hamiltonian, that we saved, so as to focus this notebook on using the algorithms. We simply load these paulis to create the original Operator.\n",
    "\n",
    "This Hamiltonian was created originally using Qiskit Chemistry for an H2 molecule at 0.735A interatomic distance. Please refer to the chemistry tutorials here if you are interested in understanding more. Suffice to say at this level Aqua does not really care about the source of the Operator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "pauli_dict = {\n",
    "    'paulis': [{\"coeff\": {\"imag\": 0.0, \"real\": -1.052373245772859}, \"label\": \"II\"},\n",
    "              {\"coeff\": {\"imag\": 0.0, \"real\": 0.39793742484318045}, \"label\": \"ZI\"},\n",
    "              {\"coeff\": {\"imag\": 0.0, \"real\": -0.39793742484318045}, \"label\": \"IZ\"},\n",
    "              {\"coeff\": {\"imag\": 0.0, \"real\": -0.01128010425623538}, \"label\": \"ZZ\"},\n",
    "              {\"coeff\": {\"imag\": 0.0, \"real\": 0.18093119978423156}, \"label\": \"XX\"}\n",
    "              ]\n",
    "}\n",
    "\n",
    "qubit_op = WeightedPauliOperator.from_dict(pauli_dict)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Let's start with a classical algorithm\n",
    "\n",
    "We can now use the Operator without regard to how it was created. We chose to start this tutorial with a classical algorithm as it involves a little less setting up than the `VQE` quantum algorithm we will use later. Here we will use `NumPyMinimumEigensolver` to compute the minimum eigenvalue of the Operator (Hamiltonian).\n",
    "\n",
    "We construct an `NumPyMinimumEigensolver` instance, passing in the Operator, and then call `run()` on in order to compute the result. All Aqua algorithms have the run method (it is defined by a base class which all algorithms extend) and while no parameters are need for classical algorithms a quantum algorithm will require a backend (quantum simulator or real device) on which it will be run. The `result` object returned is a dictionary. While the results fields can be different for algorithms solving different problems, and even within a given problem type there may be algorithm specific data returned, for a given problem the fields core to that problem are common across algorithms in order that different algorithms can be chosen to solve the same problem in a consistent fashion."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-1.8572750302023797\n"
     ]
    }
   ],
   "source": [
    "from qiskit.aqua.algorithms import NumPyMinimumEigensolver\n",
    "\n",
    "ee = NumPyMinimumEigensolver(qubit_op)\n",
    "result = ee.run()\n",
    "print(result.eigenvalue.real)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Lets switch now to using a Quantum algorithm.\n",
    "\n",
    "We will use the Variational Quantum Eigensolver (VQE) to solve the same problem as above. As its name implies its uses a variational approach. An ansatz (a variational form) is supplied and using a quantum/classical hybrid technique the energy resulting from evaluating the Operator with the variational form on a quantum backend is taken down to a minimum using a classical optimizer that varies the parameters of the variational form."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here we create the variational form and optimizer and then pass them to VQE along with the Operator. The backend is created and passed to the algorithm so it can be run there."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-1.85727503014758\n"
     ]
    }
   ],
   "source": [
    "from qiskit import BasicAer\n",
    "from qiskit.aqua.algorithms import VQE\n",
    "from qiskit.circuit.library import TwoLocal\n",
    "from qiskit.aqua.components.optimizers import L_BFGS_B\n",
    "\n",
    "var_form = TwoLocal(qubit_op.num_qubits, ['ry', 'rz'], 'cz', reps=3, entanglement='linear')\n",
    "optimizer = L_BFGS_B(maxfun=1000)\n",
    "vqe = VQE(qubit_op, var_form, optimizer)\n",
    "backend = BasicAer.get_backend('statevector_simulator')\n",
    "result = vqe.run(backend)\n",
    "print(result.eigenvalue.real)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The following shows the above but using a QuantumInstance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-1.85727503011329\n"
     ]
    }
   ],
   "source": [
    "from qiskit.aqua import QuantumInstance\n",
    "\n",
    "var_form = TwoLocal(qubit_op.num_qubits, ['ry', 'rz'], 'cz', reps=3, entanglement='linear')\n",
    "optimizer = L_BFGS_B(maxfun=1000)\n",
    "vqe = VQE(qubit_op, var_form, optimizer)\n",
    "backend = BasicAer.get_backend('statevector_simulator')\n",
    "qi = QuantumInstance(backend=backend)\n",
    "result = vqe.run(qi)\n",
    "print(result.eigenvalue.real)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Concluding\n",
    "\n",
    "This completes an introduction to programming and using Aqua algorithms. There are plenty of other  tutorials showing Aqua being used to solve other problems, including Machine Learning, Finance, Optimization and Chemistry. We encourage you to explore these further and see that various capabilities and techniques employed."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
